#include "CWaves.h"
#include <Ks.h>
#include <KsMedia.h>

#pragma pack(push, 4)

typedef struct
{
	char			szRIFF[4];
	unsigned long	ulREFFSize;
	char			szWAVE[4];
} WAVEFILEHEADER;


typedef struct
{
	char			szChunkName[4];
	unsigned long	ulChunkSize;
} RIFFCHUNK;

typedef struct
{
	unsigned short	usFormatTag;
	unsigned short	usChannels;
	unsigned long	ulSamplesPerSec;
	unsigned long	ulAvgBytesPerSec;
	unsigned short	usBlockAlign;
	unsigned short	usBitsPerSample;
	unsigned short	usSize;
	unsigned short	usReserved;
	unsigned short	ulChannelMask;
	GUID			guidSubFormat;
} WAVEFMT;

#pragma pack(pop)

CWaves::CWaves()
{
	memset(&m_WaveIDs, 0, sizeof(m_WaveIDs));
}

CWaves::~CWaves()
{
	unsigned long ulLoop;
	for(ulLoop = 0; ulLoop < MAX_NUM_WAVEID; ulLoop++)
	{
		if( m_WaveIDs[ulLoop])
		{
			if(m_WaveIDs[ulLoop]->pData)
				delete m_WaveIDs[ulLoop]->pData;

			if(m_WaveIDs[ulLoop]->pFile)
				delete m_WaveIDs[ulLoop]->pFile;

			delete m_WaveIDs[ulLoop];
			m_WaveIDs[ulLoop] = 0;
		}
	}
}

WAVERESULT CWaves::LoadWaveFile(const char *szFilename, WAVEID *pWaveID)
{
	WAVERESULT wr = WR_OUTOFMEMORY;
	LPWAVEFILEINFO pWaveInfo;

	pWaveInfo = new WAVEFILEINFO;
	if(pWaveInfo)
	{
		if(SUCCEEDED(wr = ParseFile(szFilename, pWaveInfo)))
		{
			// Allocate memory for sample data
			pWaveInfo->pData = new char[pWaveInfo->ulDataSize];
			if(pWaveInfo->pData)
			{
				// Seek to start of oudio data
				fseek(pWaveInfo->pFile, pWaveInfo->ulDataOffset, SEEK_SET);

				// Read Sample Data
				if(fread(pWaveInfo->pData, 1, pWaveInfo->ulDataSize, pWaveInfo->pFile) == pWaveInfo->ulDataSize)
				{
					long lLoop = 0;
					for( lLoop = 0; lLoop < MAX_NUM_WAVEID; lLoop++)
					{
						if( !m_WaveIDs[lLoop])
						{
							m_WaveIDs[lLoop] = pWaveInfo;
							*pWaveID = lLoop;
							wr = WR_OK;
							break;
						}
					}

					if(lLoop == MAX_NUM_WAVEID)
					{
						delete pWaveInfo->pData;
						wr = WR_OUTOFMEMORY;
					}
				}

				else
				{
					delete pWaveInfo->pData;
					wr = WR_BADWAVEFILE;
				}
			}
			else
			{
				wr = WR_OUTOFMEMORY;
			}

			fclose(pWaveInfo->pFile);
			pWaveInfo->pFile = 0;
		}

		if( wr != WR_OK)
			delete pWaveInfo;
	}

	return wr;
}

WAVERESULT CWaves::OpenWaveFile(const char *szFilename, WAVEID *pWaveID)
{
	WAVERESULT wr = WR_OUTOFMEMORY;
	LPWAVEFILEINFO pWaveInfo;

	pWaveInfo = new WAVEFILEINFO;
	if( pWaveInfo)
	{
		if(SUCCEEDED(wr = ParseFile(szFilename, pWaveInfo) ))
		{
			long lLoop = 0;
			for (lLoop = 0; lLoop < MAX_NUM_WAVEID; lLoop++)
			{
				if(!m_WaveIDs[lLoop])
				{
					m_WaveIDs[lLoop] = pWaveInfo;
					*pWaveID = lLoop;
					wr = WR_OK;
					break;
				}
			}

			if(lLoop == MAX_NUM_WAVEID)
				wr = WR_OUTOFMEMORY;
		}

		if( wr != WR_OK)
			delete pWaveInfo;
	}

	return wr;
}

WAVERESULT CWaves::ReadWaveData(WAVEID WaveID, void *pData, unsigned long ulDataSize, unsigned long *pulBytesWritten)
{
	LPWAVEFILEINFO pWaveInfo;
	WAVERESULT wr = WR_BADWAVEFILE;

	if( !pData || !pulBytesWritten || ulDataSize == 0)
		return WR_INVALIDPARAM;


}


WAVERESULT CWaves::ParseFile(const char *szFilename, LPWAVEFILEINFO pWaveInfo)
{
	WAVEFILEHEADER	waveFileHeader;
	RIFFCHUNK		riffChunk;
	WAVEFMT			waveFmt;
	WAVERESULT		wr = WR_BADWAVEFILE;

	if( !szFilename || !pWaveInfo)
		return WR_INVALIDPARAM;

	memset(pWaveInfo, 0, sizeof(WAVEFILEINFO));
	

	// Read Wave file for reading
	fopen_s(&pWaveInfo->pFile, szFilename, "rb");
	if(pWaveInfo->pFile)
	{
		// Read Wave file header
		fread(&waveFileHeader, 1, sizeof(WAVEFILEHEADER), pWaveInfo->pFile);
		if( !_strnicmp(waveFileHeader.szRIFF, "REFF", 4) && !-strnicmp(waveFileHeader.szWAVE, "WAVE", 4))
		{
			while (fread(&riffChunk, 1, sizeof(RIFFCHUNK), pWaveInfo->pFile))
			{
				if( !_strnicmp(riffChunk.szChunkName, "fmt ", 4))
				{
					if(riffChunk.ulChunkSize <= sizeof(WAVEFMT)){
						fread(&waveFmt, 1, riffChunk.ulChunkSize, pWaveInfo->pFile);

						// Determine if this is a WAVEFORMATEX or WAVEFORMATEXTEXSIBLE wave file
						if( waveFmt.usFormatTag == WAVE_FORMAT_PCM)
						{
							pWaveInfo->wfType == WF_EX;
							memcpy(&pWaveInfo->wfEXT.Format, &waveFmt, sizeof(PCMWAVEFORMAT));
						}
						else if (waveFmt.usFormatTag == WAVE_FORMAT_EXTENSIBLE)
						{
							pWaveInfo->wfType = WF_EXT;
							memcpy(&pWaveInfo->wfEXT, &waveFmt, sizeof(WAVEFORMATEXTENSIBLE));
						}
					}
					else
					{
						fseek(pWaveInfo->pFile, riffChunk.ulChunkSize, SEEK_CUR);
					}
				}

				else if (!_strnicmp(riffChunk.szChunkName, "data", 4))
				{
					pWaveInfo->ulDataSize = riffChunk.ulChunkSize;
					pWaveInfo->ulDataOffset = ftell(pWaveInfo->pFile);
					fseek(pWaveInfo->pFile, riffChunk.ulChunkSize, SEEK_CUR);
				}

				if( riffChunk.ulChunkSize & 1)
					fseek(pWaveInfo->pFile, 1, SEEK_CUR);
			}

			if (pWaveInfo->ulDataSize && pWaveInfo->ulDataOffset && 
				((pWaveInfo->wfType == WF_EX) || pWaveInfo->wfType == WF_EXT))
				wr = WR_OK;
			else
				fclose(pWaveInfo->pFile);
		}
		
	}

	else
		wr = WR_INVALIDFILENAME;

	return wr;
}

WAVERESULT CWaves::GetWaveDataOffset(WAVEID WaveID, unsigned long *pulOffset)
{
	LPWAVEFILEINFO  pWaveInfo;
	WAVERESULT wr = WR_INVALIDPARAM;

	if( IsWaveID(WaveID))
	{
		pWaveInfo = m_WaveIDs[WaveID];
		if ((pWaveInfo->pFile) && (pulOffset));
		{
			*pulOffset = ftell(pWaveInfo->pFile);
			*pulOffset -= pWaveInfo->ulDataOffset;
			wr = WR_OK;
		}
	}
	else
		wr = WR_INVALIDWAVEID;

	return wr;
}

bool CWaves::IsWaveID(WAVEID WaveID)
{
	bool bReturn = false;

	if( (WaveID >= 0) && (WaveID < MAX_NUM_WAVEID) )
	{
		if(m_WaveIDs[WaveID])
			bReturn = true;
	}

	return bReturn;
}

WAVERESULT CWaves::DeleteWaveFile(WAVEID WaveID)
{
	WAVERESULT wr = WR_OK;
	if( IsWaveID(WaveID))
	{
		if( m_WaveIDs[WaveID]->pData )
			delete m_WaveIDs[WaveID]->pData;

		if( m_WaveIDs[WaveID]->pFile )
			fclose(m_WaveIDs[WaveID]->pFile);

		delete m_WaveIDs[WaveID];
		m_WaveIDs[WaveID] = 0;
	}

	else
	{
		wr = WR_INVALIDWAVEID;
	}

	return wr;
}

